#!/usr/bin/env python
# -*- coding:utf8 -*-
"""
This module was created to parse lines with some particularities.
The data returned will be a dictionary, all method return something, never get an error.
"""
import re, sys
import UserDict

def reorder(e):
	if isinstance(e, bool):
		return e
	elif isinstance(e, list):
		return " ".join(reversed(e)).strip()

class GetParams:
	def __init__(self, text=None):
		self.opts = {}
		self.text = text
		self.sep = ";"
		self.delimiter = "-"
		self.required = {}
		self.aliases = {}
		self.get_params()

	def __call__(self):
		return self.opts

	def __getitem__(self, key):
		if not self.opts.has_key(key):
			self.opts[key]=False
		return self.opts[key]

	def __setitem__(self, key, value):
		self.opts.update({key: value})

	def __delitem__(self, key):
		if self.opts.has_key(key):
			del self.opts[key]

	def __repr__(self):
		return "%s%s\nArguments%s" %(self.__class__.__name__, repr(self.opts), repr(self._arguments))

	def __contains__(self, key):
		return self.opts.has_key(key)

	def __len__(self):
		return len(self.opts)

	def sum(self):
		return '%s(%d keys, %d req keys, %d aliases)' \
		% (self.__class__.__name__, len(self.opts), len(self.required), len(self.aliases))

	def get(self, key, fail=None):
		return self.opts.get(key, fail)

	def has_key(self, key):
		return self.opts.has_key(key)

	def keys(self):
		return self.opts.keys()

	def values(self):
		return self.opts.values()

	def get_params(self):
		#2010-03-25 00:37:29
		line = self.text or " ".join(sys.argv[1:])
		values=[]; keys=[]; unnamed = []; temp = []
		join_args = False; insensitive = False
		self._action=None
		self._arguments={}
		self._modifiers={"i": insensitive, "j": join_args}
		for token in reversed(line.split()):
			if not token[0] in "-+.:":
				values.append(token)
				temp.append(token)
			if token.startswith("-"):
				temp=[]
				token=token[1:]
				keys.append(token)
				if ":" in token:
					token, value=token.split(":",1)
					values.append(value)
				if values == []:
					if token.startswith("!"):
						token=token[1:]
						values=False
					else:
						values=True
				if self.opts.has_key(token):
					if isinstance(self.opts[token], (basestring, bool)):
						self.opts[token]=[self.opts[token], reorder(values)]
					elif isinstance(self.opts[token], list):
						self.opts[token].append(reorder(values))
				else:
					if isinstance(values, list):
						self.opts[token]=" ".join(reversed(values)).strip()
					else:
						self.opts[token]=values
				values=[]
			elif token.startswith("+"):
				token = token[1:]
				if temp:
					token = " ".join([token]+list(reversed(temp)))
				unnamed.append(token)
				for t in temp:
					if values: values.pop()
				temp=[]
			elif token.startswith("."):
				token = token[1:]
				if token == "j":
					#space works as separator if %u% is the only key for the option dict
					#if join_args is False, then space doesn't split string
					join_args = True
					self._modifiers.update({token: join_args})
				if token == "i":
					#if insensitive is True, options are insensitive case. -t 1 -T 3 = "t": [1, 3]
					insensitive = True
					self._modifiers.update({token: insensitive})
				if len(token)>1 and token.startswith("s"):
					self.sep = self._modifiers["s"] = token[1:]
			elif token.startswith(":"):
				if temp:
					unnamed.append(" ".join(list(reversed(temp))))
					for t in temp:
						if values: values.pop()
					temp=[]
				self._action=token[1:]

		if values != []:
			self._arguments = " ".join(reversed(values))
			if unnamed:
				self._arguments = list(reversed(unnamed)) + [self._arguments]
		elif unnamed:
			self._arguments = list(reversed(unnamed))
		for key in self.opts:
			if isinstance(self.opts[key], basestring) and self.sep in self.opts[key]:
				self.opts[key] = self.opts[key].split(self.sep)
				tokens = []
				for token in self.opts[key]:
					if token == "": continue
					tokens.append(token.strip())
				self.opts[key] = tokens
			elif isinstance(self.opts[key], list):
				i = 0
				for s in self.opts[key]:
					if isinstance(s, basestring):
						i += 1
						strings=list(reversed(s.split(self.sep)))
						if len(strings)>1:
							for sub in strings:
								if sub:
									self.opts[key].insert(i, sub.strip())
									i += 1
							self.opts[key].remove(s)
				self.opts[key] = list(reversed(self.opts[key]))

		if join_args and self._arguments and isinstance(self._arguments, basestring) and len(self.opts) == 0:
			self._arguments = self._arguments.split()
		for opt in self.opts:
			if isinstance(self.opts[opt], list) and len(self.opts[opt]) == 1:
				self.opts[opt] = self.opts[opt][0]
		opts = self.opts.copy()
		if self._modifiers["i"]:
			for opt in self.opts:
				if opt.islower(): continue
				if self.opts.has_key(opt.lower()):
					if isinstance(self.opts[opt.lower()], list):
						if isinstance(self.opts[opt], list):
							opts[opt.lower()] +=  opts.pop(opt)
						else:
							opts[opt.lower()].append(opts.pop(opt))
					else:
						if isinstance(self.opts[opt], list):
							opts[opt.lower()] = [opts[opt.lower()]] + opts.pop(opt)
						else:
							opts[opt.lower()] = [opts[opt.lower()], opts.pop(opt)]
			self.opts=opts
		return self.opts

	def check_params(self):
		err_msg=[]

		for alias in self.aliases:
			values=[]
			if alias in self.opts:
				if isinstance(self.opts[alias], list):
					values += self.opts[alias]
				else:
					values = [self.opts[alias]]
			for key in self.aliases[alias]:
				if key in self.opts:
					if isinstance(self.opts[key], list):
						values += self.opts[key]
					else:
						values.append(self.opts[key])
					del self.opts[key]
			if values:
				self.opts[alias] = values

		for opt in self.required:
			if opt not in self.opts:
				err = "OPTION: -%s %s\nSETTING %s AS %s\nHELP: %s\n" % (opt, self.required[opt][2], opt, self.required[opt][0], self.required[opt][1])
				self.opts[opt] = self.required[opt][0]
				print (err)
				err_msg.append(err)
		for opt in self.opts:
			self.opts[opt] = self.opts[opt][0]
		return err_msg

	def add_required(self, option, def_value=True, help_msg="This parameter is not optional.", err_msg="is required"):
		self.required.update({option: [def_value, help_msg, err_msg]})

	def add_alias(self, option, *args):
		if self.aliases.has_key(option):
			self.aliases[option] += args
		else:
			self.aliases[option] = args

	def cense(self):
		return len(self.opts), len(self._arguments), len(self._action), len(self._modifiers)

	def action(self):
		return self._action

	def arguments(self):
		return self._arguments

	def parameters(self):
		return self.opts

	def modifiers(self):
		return self._modifiers
